import sys
import io
import os
import threading
import time
import re
import hashlib
import httpx
from pathlib import Path
from mcp.server.fastmcp import FastMCP
from datetime import datetime
import execjs
import json
import pygame
from bs4 import BeautifulSoup
import aiohttp
import logging
from urllib.parse import quote

# --- 基础设置与初始化 ---
# 解决中文乱码
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

# 全局配置和状态
GLOBAL_CONFIG = {
    'DOWNLOAD_DIR': os.getenv('DOWNLOAD_DIR', 'D:/1'),
    'BILIBILI_COOKIE': "buvid3=93F12A49-F981-A1BA-0AED-15F0D142A79886204infoc; b_nut=1761553986; _uuid=E10F10E877-9EC2-1273-CD94-9D69B1C38D6D87262infoc; enable_web_push=DISABLE; buvid_fp=1fed7b07500755e95e1beb048c9bb0df; buvid4=56A6D30D-9A98-A987-2B51-47CCDC6B257686954-025102716-utYYOJL/XC9Jt+w4cwQwmg%3D%3D; SESSDATA=60eef209%2C1777170984%2C5291e%2Aa2CjA9mXUMkzvODcTYlLOoWzn1kzivtVrpv1umuWM4oQDoh5dzEPcDb4FI9UsEoqGUDfASVk5peV9ISWVvMEtVUENtd2gxRkZfbjNPZ0haLThpbDFSR2RlUXJPVVF6ZXRUMGRZRHo2emRtTWxRWGdKMmRMZDNjZ1RCRjItbVNJa3hRZkxJemlrTVJ3IIEC; bili_jct=897141e18595e2e15d066305bcfbcde7; DedeUserID=214438039; DedeUserID__ckMd5=2ecf106214ccb391; theme-tip-show=SHOWED; rpdid=|(k))k)~~JYl0J'u~YuYkuYYu; CURRENT_QUALITY=0; theme-avatar-tip-show=SHOWED; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NzcxNzI2ODAsImlhdCI6MTc3NjkxMzQyMCwicGx0IjotMX0.1a3h2C1c5y2oM4a0r3y7U8lQdG9mS5wWfWfQxXh1x8A; bili_ticket_expires=1777172680; b_lsid=87153B3B_19A520B54AA; sid=4qal6z3f; home_feed_column=4; browser_resolution=925-1063; CURRENT_FNVAL=4048; bp_t_offset_214438039=1131616293565235200" ,
    'NETEASE_MUSIC_U': "003F10671D76BC8C7A2BC0866FF486DF6B4430EAE3CAE40FE36D5EF4AFF363F3ABB9102BFFDEE839915D63660015BA278268767349DE13041B2F3174ACF3B54996C1C0B425D7BD6190BB465543E11341EB0F43C9B1194FEADBC2C4A9ACC7C9D3660D96EA6FA8F421075FA822065D49CCC401CB7CD9636165704221223B1069C56B4AA823B0F4B6F767449F0D104938C4ADD7C6E6053AF2F7952C8E872C1E7BA2296F9E0B3B4A2B17ACBD3E1B7B583159A8775822717626D354C20899E79B73B362DE09E07C144877FFB85092D75E750C21015BAA0C6AE5F2584FEC734783DE0F49E23861DB8D7C1DDEB6672EDF7F163EA9F9B4A243C917893363ACDC8263891B6F413B875D2E36198D518F8F63B52E1A330631C2573BE84333D2A47D30A2F23254396F90E4C24C61E48148DF1D482BDFE117C49B97CB24DB0643DCC231AAC585D033F33BE9E560468533CC7FA32A508AE886F761779610A8E9D298EEAF8546ABE8F20ABC9CF816021B202975AEB13EBAADBBC9E4F87C4649CE001E323874208A7BB3E868D04A80B8288E60DC7D6594B86CD11162146FF95ED77ECC08F7BEBFAECB",
    'NETEASE_CSRF_TOKEN': "2f6dc3c9033814e59eafb23e8e374727",
    'NETEASE_JS_PATH': 'wyy.js'
}

audio_thread = None
is_playing = False
stop_playing = False
current_file = None

# 日志配置 (全局一次性配置)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 初始化pygame mixer
try:
    pygame.mixer.pre_init(frequency=22050, size=-16, channels=2, buffer=512)
    pygame.mixer.init()
    print("✅ pygame mixer 初始化成功")
except Exception as e:
    print(f"⚠️ pygame mixer 初始化失败: {e}")


def create_mcp_server(port: int = 7888) -> FastMCP:
    """创建并配置MCP服务器，工具函数依赖 GLOBAL_CONFIG"""
    mcp = FastMCP(name="unified_mcp_server", port=port)

    @mcp.tool()
    async def extract_bilibili_subtitles(bvid: str, prefer_lang: str = 'ai-zh') -> str:
        """通过 BVID 异步查询 Bilibili 视频的 AI 字幕内容。"""
        # 从全局配置中获取 Cookie
        cookie = GLOBAL_CONFIG['BILIBILI_COOKIE']
        if "YOUR_VALID_BILIBILI_COOKIE_HERE" in cookie:
             return "❌ 工具未配置：请在主函数中设置有效的 BILIBILI_COOKIE。"
             
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0",
            "Referer": "https://www.bilibili.com/",
            "Cookie": cookie
        }
        
        async with aiohttp.ClientSession(headers=headers) as session:
            # --- 1. 获取 AID 和 CID ---
            aid_url = f"https://api.bilibili.com/x/web-interface/view?bvid={bvid}"
            async with session.get(aid_url, timeout=10) as resp_aid:
                if resp_aid.status != 200:
                    return f"❌ 步骤 1 失败: 获取 AID 时 HTTP 错误 {resp_aid.status}"
                aid_data = await resp_aid.json()
                if aid_data.get('code') != 0 or not aid_data.get('data'):
                    return f"❌ 步骤 1 失败: AID API 响应错误: {aid_data.get('message', '未知错误')}"
                
                aid = aid_data['data']['aid']
                logging.info(f"-> 成功获取 AID: {aid}")
                
                cid_url = f"https://api.bilibili.com/x/player/pagelist?bvid={bvid}&jsonp=jsonp"
                async with session.get(cid_url, timeout=10) as resp_cid:
                    if resp_cid.status != 200:
                        return f"❌ 步骤 1 失败: 获取 CID 时 HTTP 错误 {resp_cid.status}"
                    cid_data = await resp_cid.json()
                    if cid_data.get('code') != 0 or not cid_data.get('data') or not cid_data['data']:
                        return f"❌ 步骤 1 失败: CID API 响应错误: {cid_data.get('message', '未知错误')}"
                    cid = cid_data['data'][0]['cid']
                    logging.info(f"-> 成功获取 CID: {cid}")

            # --- 2. 请求 V2 API (获取字幕 URL) ---
            v2_api_url = f"https://api.bilibili.com/x/player/wbi/v2?aid={aid}&cid={cid}"
            async with session.get(v2_api_url, timeout=10) as resp_v2:
                if resp_v2.status != 200:
                    return f"❌ 步骤 2 失败: V2 API 请求错误 {resp_v2.status} (Cookie/WBI 问题)"
                response_v2_text = await resp_v2.text()

            # --- 3. 字幕内容提取与清理 ---
            try:
                data_v2 = json.loads(response_v2_text)
                if data_v2.get('code') != 0:
                    return f"❌ 步骤 3 失败: V2 JSON 响应返回错误代码: {data_v2.get('code')}"

                subtitles = data_v2.get('data', {}).get('subtitle', {}).get('subtitles')
                if not subtitles:
                    return "⚠️ 视频没有找到任何字幕信息。"
                    
                target_sub = next((sub for sub in subtitles if sub.get('lan') == prefer_lang), 
                                  next((sub for sub in subtitles if sub.get('lan') == 'zh'), subtitles[0]))
                
                relative_url = target_sub.get('subtitle_url')
                if not relative_url:
                    return "❌ 步骤 3 失败: 无法提取 'subtitle_url'。"
                    
                ai_subtitle_url = "https:" + relative_url if relative_url.startswith('//') else relative_url

                async with session.get(ai_subtitle_url, timeout=15) as response_sub:
                    response_sub.raise_for_status()
                    subtitle_data = await response_sub.json()

            except Exception as e:
                return f"❌ 步骤 3 失败: 字幕文件请求或解析出错: {e}"

            all_contents = []
            lyric_pattern = re.compile(r'♪.*♪') 

            for item in subtitle_data.get('body', []):
                content = item.get('content')
                if content:
                    cleaned_content = lyric_pattern.sub('', content).strip()
                    if cleaned_content:
                        all_contents.append(cleaned_content)
                    
            final_text = "，".join(all_contents)
            
            if not final_text:
                return "⚠️ 字幕内容为空或清理后无有效文本。"
                
            return f"✅ 成功提取 {bvid} 的字幕：\n{final_text}"

    @mcp.tool()
    async def search_netease_music(keyword: str) -> str:
        """网易云音乐搜索工具: 根据关键词搜索歌曲。"""
        config = GLOBAL_CONFIG
        
        if "YOUR_VALID_MUSIC_U_COOKIE_HERE" in config['NETEASE_MUSIC_U']:
             return "❌ 工具未配置：请在主函数中设置有效的 NETEASE_MUSIC_U Cookie。"

        headers={'referer':'https://music.163.com/', 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'}
        cookies = {'MUSIC_U': config['NETEASE_MUSIC_U']}
        
        i1x = {
            "hlpretag": "<span class=\"s-fc7\">", "hlposttag": "</span>", "s": keyword, "type": "1",
            "offset": "0", "total": "true", "limit": "5", "csrf_token": config['NETEASE_CSRF_TOKEN']
        }
        url='https://music.163.com/weapi/cloudsearch/get/web'

        try:
            if not Path(config['NETEASE_JS_PATH']).exists():
                return f"错误：找不到加密脚本文件 {config['NETEASE_JS_PATH']}。"
            
            with open(config['NETEASE_JS_PATH'], encoding='utf-8') as f:
                js_code = execjs.compile(f.read())
                r = js_code.call('GetSign', i1x)
                data = {"params": r.get("encText"), "encSecKey": r.get("encSecKey")}

            async with httpx.AsyncClient(timeout=10.0) as client:
                response = await client.post(url=url, data=data, headers=headers, cookies=cookies)
                response.raise_for_status()

            result = json.loads(response.text)
            if result.get("code") == 200 and "songs" in result["result"]:
                songs_info = []
                for song in result["result"]["songs"]:
                    privilege = song.get("privilege", {})
                    is_vip = (privilege.get("fee") == 1 and privilege.get("pl") == 0)
                    
                    if not is_vip:
                        singers = ",".join([ar["name"] for ar in song["ar"]])
                        duration = song["dt"] // 1000
                        
                        songs_info.append(f"""
-{'-' * 50}
歌曲名称: {song['name']}
歌曲ID: {song['id']}
歌曲歌手: {singers}
歌曲时长: {duration // 60}:{duration % 60:02d}
是否VIP歌曲: 否
-{'-' * 50}""")
                
                return "\n".join(songs_info) if songs_info else "未找到非VIP歌曲。"
            else:
                return f"搜索失败：{result.get('message', '未知错误')}"
                
        except Exception as e:
            return f"发生未知错误：{type(e).__name__}: {str(e)}"

    @mcp.tool()
    async def parse_weather(city: str, days: int = 1) -> str:
        """查询指定城市的天气信息，返回指定天数（1-14天）的天气预报"""
        days = max(1, min(14, days))
        
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Referer': 'https://www.baidu.com/'
        }

        try:
            url = f"https://weathernew.pae.baidu.com/weathernew/pc?query={quote(f'{city}天气')}&srcid=4982&forecast=long_day_forecast"
            
            async with aiohttp.ClientSession() as session:
                async with session.get(url, headers=headers, timeout=15.0) as resp:
                    resp.raise_for_status()
                    html = await resp.text()
                
            soup = BeautifulSoup(html, 'lxml')
            script_tag = soup.find('script', text=re.compile(r'window\.tplData'))
            
            if not script_tag:
                return "解析失败：未找到天气数据 (网站结构可能已变更)"
                
            tpl_data_match = re.search(r'window\.tplData\s*=\s*({.*})', script_tag.get_text(), re.DOTALL)
            if not tpl_data_match:
                 return "解析失败：未找到 window.tplData JSON 数据"
            
            forecast_data = json.loads(tpl_data_match.group(1))
            forecast = forecast_data.get("long_day_forecast", {}).get("info", [])
            
            if not forecast:
                return f"未获取到{city}的天气预报"
            
            result = []
            for i in range(min(days, len(forecast))):
                data = forecast[i]
                result.append(
                    f"[{'今日' if i == 0 else '第' + str(i+1) + '天'}] "
                    f"日期: {data.get('date', '未知')}, "
                    f"天气: {data.get('weather_day') or data.get('weather', '未知')}, "
                    f"温度: {data.get('temperature_night', '未知')}°C~{data.get('temperature_day', '未知')}°C"
                )
            
            return '\n'.join(result)
        
        except Exception as e:
            return f"天气查询过程中发生内部错误：{type(e).__name__}: {str(e)}"


    @mcp.tool()
    async def download_files(url: str ,filename: str) -> str:
        """下载文件工具: 从URL下载文件到指定目录"""
        download_path = Path(GLOBAL_CONFIG['DOWNLOAD_DIR'])
        download_path.mkdir(parents=True, exist_ok=True)
        
        if not filename or '.' not in filename:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"download_{timestamp}.bin"
        
        file_path = download_path / filename
        
        # 文件名冲突处理
        counter = 1
        original_path = file_path
        while file_path.exists():
            name, ext = os.path.splitext(original_path.name)
            file_path = download_path / f"{name}_{counter}{ext}"
            counter += 1
        
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'}
        
        try:            
            async with httpx.AsyncClient(timeout=60.0, follow_redirects=True) as client:
                async with client.stream('GET', url, headers=headers) as response:
                    response.raise_for_status()
                    
                    total_size = int(response.headers.get('content-length', 0))
                    downloaded_size = 0
                    
                    with open(file_path, 'wb') as f:
                        async for chunk in response.aiter_bytes(chunk_size=8192):
                            f.write(chunk)
                            downloaded_size += len(chunk)
                            
                            if total_size > 0 and downloaded_size % (1024 * 1024) == 0:
                                progress_percent = (downloaded_size / total_size) * 100
                                print(f"下载进度: {downloaded_size / 1024 / 1024:.1f}MB ({progress_percent:.1f}%)")
            
            file_size_mb = file_path.stat().st_size / 1024 / 1024
            
            return (
                f"✅ 下载完成！\n"
                f"📁 保存位置: {file_path.absolute()}\n"
                f"📊 文件大小: {file_size_mb:.2f} MB"
            )
                
        except Exception as e:
            return f"❌ 下载失败：{type(e).__name__} - {str(e)}"

    @mcp.tool()
    async def get_nowtime() -> str:
        """获取当前时间,格式为: 2025-10-15 10:00:00"""
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    @mcp.tool()
    async def parse_netease_music(music_url: str) -> str:
        """网易云音乐解析接口: 根据链接返回标题与直链。"""
        id_match = re.search(r'(?:/song|\#/song)\?id=(\d+)', music_url)
        if not id_match:
            return "解析失败：无效的网易云音乐链接，未找到歌曲 ID。"

        song_id = id_match.group(1)
        download_url_template = f'https://music.163.com/song/media/outer/url?id={song_id}'

        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Referer': 'https://music.163.com',
        }

        try:
            async with httpx.AsyncClient(timeout=10.0, follow_redirects=True) as client:
                # 1. 获取标题
                response = await client.get(music_url, headers=headers)
                response.raise_for_status()

                title_match = re.search(r'<title>(.*?)</title>', response.text, re.DOTALL)
                title = title_match.group(1).split('-')[0].strip() if title_match else f'歌曲ID_{song_id}'
                
                # 2. 尝试获取真实的下载链接（HEAD请求）
                final_url = download_url_template
                try:
                    head_response = await client.head(download_url_template, headers=headers)
                    final_url = str(head_response.url)
                    if 'music.163.com' not in final_url:
                        download_url_template = final_url
                except:
                    pass
                
                return (
                    f"标题: {title}\n"
                    f"直链: {download_url_template}\n\n"
                    f"💡 提示：可以使用 download_files 工具直接下载此文件"
                )

        except Exception as e:
            return f"请求失败或解析错误：{type(e).__name__}"

    @mcp.tool()
    async def parse_video_url(video_url: str) -> str:
        """解析抖音或B站的视频链接，返回标题和媒体链接"""
        if not re.search(r'(douyin\.com|bilibili\.com)', video_url):
            return "解析失败：请输入一个有效的抖音或B站视频链接。"

        api_url = 'https://api.snapany.com/v1/extract'
        
        locale = 'zh'
        qc = '6HTugjCXxR'
        timestamp = str(int(time.time() * 1000))
        combined_string = f"{video_url}{locale}{timestamp}{qc}"
        g_footer = hashlib.md5(combined_string.encode('utf-8')).hexdigest()

        headers = {
            'accept': '*/*', 'accept-language': 'zh', 'content-type': 'application/json',
            'origin': 'https://snapany.com', 'referer': 'https://snapany.com/',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0',
            'g-footer': g_footer, 'g-timestamp': timestamp,
        }
        payload = {'link': video_url}

        try:
            async with httpx.AsyncClient(timeout=30.0) as client:
                response = await client.post(api_url, headers=headers, json=payload)
                response.raise_for_status()
            data = response.json()

            title = data.get('text', '无标题')
            medias = data.get('medias', [])

            if not medias:
                return f"解析成功，但未能找到媒体文件。\n标题: {title}"

            video_info = medias[0]
            result_video_url = video_info.get('resource_url', '未找到')
            preview_url = video_info.get('preview_url', '未找到')
            audio_url = medias[-1].get('resource_url') if len(medias) > 1 else '无音频'

            return (
                f"标题: {title}\n"
                f"视频直链: {result_video_url}\n"
                f"音频直链: {audio_url}\n"
                f"视频封面: {preview_url}"
            )
        except Exception as e:
            return f"解析失败：{type(e).__name__}"

    @mcp.tool()
    async def play_music(file_path: str) -> str:
        """播放音乐文件工具: 使用pygame播放本地音频文件"""
        global audio_thread, is_playing, stop_playing, current_file
        
        if not os.path.exists(file_path):
            return f"❌ 文件不存在: {file_path}"
        
        if is_playing:
            await stop_music()
            time.sleep(0.5)
        
        try:
            stop_playing = False
            is_playing = True
            current_file = file_path
            
            def play_audio():
                global is_playing, stop_playing
                try:
                    logging.info(f"🎵 开始播放文件: {file_path}")
                    pygame.mixer.music.load(file_path)
                    pygame.mixer.music.play()
                    
                    while pygame.mixer.music.get_busy() and not stop_playing:
                        time.sleep(0.1)
                    
                    if stop_playing:
                        pygame.mixer.music.stop()
                        logging.info("⏹️ 音频播放被手动停止")
                    else:
                        logging.info("✅ 音频播放完成")
                    
                except Exception as e:
                    logging.error(f"❌ 播放音频文件时出错: {e}")
                finally:
                    is_playing = False
                    stop_playing = False
                    
            audio_thread = threading.Thread(target=play_audio, daemon=True)
            audio_thread.start()
            
            time.sleep(0.2)
            
            return f"🎵 开始播放: {os.path.basename(file_path)}\n🎮 播放器状态: {'忙碌' if pygame.mixer.music.get_busy() else '空闲'}"
            
        except Exception as e:
            is_playing = False
            return f"❌ 播放失败: {str(e)}"

    @mcp.tool()
    async def stop_music() -> str:
        """停止音乐播放工具: 停止当前正在播放的音频"""
        global is_playing, stop_playing, current_file, audio_thread
        
        if not is_playing:
            return "ℹ️ 当前没有正在播放的音乐"
        
        try:
            stop_playing = True
            pygame.mixer.music.stop()
            
            if audio_thread and audio_thread.is_alive():
                audio_thread.join(timeout=2.0)
            
            is_playing = False
            stop_playing = False
            current_file = None
            
            return "⏹️ 音乐播放已停止"
            
        except Exception as e:
            logging.error(f"❌ 停止播放时出错: {e}")
            return f"❌ 停止播放时出错: {str(e)}"

    @mcp.tool()
    async def get_audio_status() -> str:
        """获取音频播放状态工具: 查看当前音频播放状态"""
        global is_playing, stop_playing, current_file
        
        status = "🎵 音频播放状态:\n"
        status += f"   标记为播放中: {'是' if is_playing else '否'}\n"
        status += f"   停止标志: {'是' if stop_playing else '否'}\n"
        status += f"   当前文件: {os.path.basename(current_file) if current_file else '无'}\n"
        
        pygame_busy = pygame.mixer.music.get_busy()
        status += f"   pygame播放器: {'忙碌中' if pygame_busy else '空闲'}\n"
        
        if is_playing != pygame_busy:
            status += "   ⚠️ 状态不一致，请注意！\n"
        else:
            status += " ✅ 状态同步。\n"
        
        return status

    @mcp.tool()
    async def test_audio_system() -> str:
        """测试音频系统工具: 检查音频系统是否正常工作"""
        result = "🔧 音频系统测试结果:\n"
        
        try:
            mixer_init = pygame.mixer.get_init()
            result += f"   Mixer 初始化: {mixer_init if mixer_init else '未初始化'}\n"
            result += f"   pygame版本: {pygame.version.ver}\n"
        except Exception:
            result += "   Mixer 检查失败。\n"

        test_audio_file = "Hello, I am Javis..mp3"
        
        if not os.path.exists(test_audio_file):
            result += f"   ⚠️ 测试音频文件不存在: {test_audio_file}\n"
        else:
            try:
                pygame.mixer.music.load(test_audio_file)
                pygame.mixer.music.play()
                start_time = time.time()
                while pygame.mixer.music.get_busy() and (time.time() - start_time) < 3:
                    time.sleep(0.1)
                pygame.mixer.music.stop()
                result += "   ✅ 测试音频播放: 成功播放并停止。\n"
            except Exception as e:
                result += f"   ❌ 测试音频播放: 失败 ({str(e)})\n"
        
        return result
    
    return mcp


def run_mcp_server(port: int = 7888):
    """运行MCP服务器"""
    print("🚀 启动MCP HTTP服务器")
    print(f"🌐 服务地址: http://127.0.0.1:{port}")
    print("📌 按 CTRL+C 停止服务")
    
    mcp = create_mcp_server(port)
    
    try:
        mcp.run(transport='streamable-http')
    except KeyboardInterrupt:
        print("\n🛑 服务器已停止")
    except Exception as e:
        print(f"\n❌ 服务器启动失败: {e}")

def start_server_in_background(port: int = 7888):
    """在后台启动MCP服务器"""
    def run_server():
        run_mcp_server(port)
    
    server_thread = threading.Thread(target=run_server, daemon=True)
    server_thread.start()
    time.sleep(2)  # 等待服务器启动
    return server_thread

if __name__ == "__main__":
    run_mcp_server()